package ms.unicorn.diagram.edit.helpers;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import ms.unicorn.EPhantomAttribute;
import ms.unicorn.Element;
import ms.unicorn.UnicornPackage;
import ms.unicorn.diagram.parsers.MessageFormatParser;
import ms.unicorn.dsl.HintedSpecializationType;
import ms.unicorn.dsl.IEditHelperAdviceEx;
import ms.unicorn.dsl.IElementField;
import ms.unicorn.dsl.ILanguageElement;
import ms.unicorn.dsl.unicornDSL.XExpression;
import ms.unicorn.dsl.utils.KeyList;
import ms.unicorn.util.FigureConstant;
import ms.unicorn.util.ViewUtil;

import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.transaction.util.TransactionUtil;
import org.eclipse.gef.EditPart;
import org.eclipse.gmf.runtime.common.core.command.CommandResult;
import org.eclipse.gmf.runtime.common.core.command.ICommand;
import org.eclipse.gmf.runtime.common.ui.services.parser.IParser;
import org.eclipse.gmf.runtime.diagram.core.preferences.PreferencesHint;
import org.eclipse.gmf.runtime.emf.core.util.EMFCoreUtil;
import org.eclipse.gmf.runtime.emf.type.core.IElementType;
import org.eclipse.gmf.runtime.emf.type.core.commands.ConfigureElementCommand;
import org.eclipse.gmf.runtime.emf.type.core.edithelper.AbstractEditHelperAdvice;
import org.eclipse.gmf.runtime.emf.type.core.requests.ConfigureRequest;
import org.eclipse.gmf.runtime.emf.type.core.requests.IEditCommandRequest;
import org.eclipse.gmf.runtime.notation.View;

public abstract class LanguageElementAdvice extends AbstractEditHelperAdvice implements IEditHelperAdviceEx, FigureConstant {

	private static Map<String, EAttribute> builtinFields;
	private static Map<String, IParser> parsers;
	
	static {		
		builtinFields = new HashMap<String, EAttribute>(5);
		builtinFields.put("NAME", UnicornPackage.eINSTANCE.getElement_Name());
		builtinFields.put("TEXT", UnicornPackage.eINSTANCE.getElement_Text());
		builtinFields.put("DESCRIPTION", UnicornPackage.eINSTANCE.getElement_Description());
		builtinFields.put("STEREOTYPE", UnicornPackage.eINSTANCE.getElement_Stereotype());
		
		parsers = new HashMap<String, IParser>();
	}
	
	public LanguageElementAdvice() {
		super();	
	}
	
	@Override
	public ICommand getAfterEditCommand(IEditCommandRequest request) {
		if (request instanceof ConfigureRequest) {
			request.setParameter(IEditCommandRequest.REPLACE_DEFAULT_COMMAND, Boolean.TRUE);
			return new ConfigureElementCommand((ConfigureRequest) request) {
		
				@Override
				protected CommandResult doExecuteWithResult(
						IProgressMonitor monitor, IAdaptable info)
						throws ExecutionException {
					
					ConfigureRequest request = (ConfigureRequest) this.getRequest();
					Element domainElement = (Element) request.getElementToConfigure();
					IElementType type = request.getTypeToConfigure();
					if (type instanceof HintedSpecializationType)
						configElement(domainElement, request);
					return CommandResult.newOKCommandResult();
				}
			};
		}
		return null;
	}
	
	@Override
	public EditPart createEditPart(EditPart ep, Object model) {
		return null;
	}
	
	public void configElement(Element element, ConfigureRequest request) {
		IElementType type = request.getTypeToConfigure();
		if (type instanceof HintedSpecializationType) {
			HintedSpecializationType hintType = (HintedSpecializationType) type;
			element.setElementType(type.getId());
			element.setLanguage(hintType.getLanguage().getId());
			
			//initialize custom fields of the element
			ILanguageElement e = hintType.getElement();
			for (IElementField field: e.getFields()) {
				EPhantomAttribute attr = field.getAttribute();
				if (!(field.getDefaultValue() instanceof XExpression))
					attr.setValue(element, field.getDefaultValue());
			}
			// run custom initialization
			e.configElement(element);
		}
	}
	
	@Override
	public IParser createParser(View view, Element obj, ILanguageElement le) {
		String name = ViewUtil.getViewParser(view);
		String qualifiedName = ViewUtil.getQualifiedViewName(view) + "@" + name;
		
		MessageFormatParser parser = (MessageFormatParser) parsers.get(qualifiedName);
		if (parser == null) {
			List<EAttribute> viewFeatures = new ArrayList<EAttribute>();
			KeyList param = le.getElementProperty(name, null);
			
			Object[] viewFields = (Object[]) param.getByKey("fields");
			
			if (viewFields != null)
				for(Object field: viewFields) {
					if (field instanceof IElementField)
						viewFeatures.add(((IElementField) field).getAttribute());
					else if (field instanceof String) {
						String sField = ((String) field).toUpperCase();
						IElementField ef = le.getField((String) field);
						if (ef != null) viewFeatures.add(ef.getAttribute());
						else if (builtinFields.containsKey(sField)) 
							viewFeatures.add(builtinFields.get(sField));
					}
				}
			
			List<EAttribute> editFeatures = new ArrayList<EAttribute>();
			Object[] editFields = (Object[]) param.getByKey("edit.fields");
			if (editFields != null)
				for(Object field: viewFields) {
					if (field instanceof IElementField)
						editFeatures.add(((IElementField) field).getAttribute());
					else if (field instanceof String) {
						String sField = ((String) field).toUpperCase();
						IElementField ef = le.getField((String) field);
						if (ef != null) editFeatures.add(ef.getAttribute());
						else if (builtinFields.containsKey(sField)) 
							editFeatures.add(builtinFields.get(sField));
					}
				}
			
			EAttribute[] features = viewFeatures.toArray(new EAttribute[viewFeatures.size()]);
			EAttribute[] editableFeatures = 
					editFeatures.size() > 0 ? editFeatures.toArray(new EAttribute[editFeatures.size()]):
						features;
			
			String viewPattern = (String)param.getByKey("view");
			if (viewPattern == null)
				viewPattern = "{0}";
			String editPattern = (String)param.getByKey("edit");
			if (editPattern == null || editPattern.length() == 0)
				editPattern = viewPattern;
					
			parser = new MessageFormatParser(features,	editableFeatures);
			parser.setViewPattern(viewPattern);
			parser.setEditorPattern(editPattern);
			parser.setEditPattern(editPattern);		
			
			parsers.put(qualifiedName, parser);
		}
		return parser;
	}
	
	@Override
	public View createView(ILanguageElement element, IAdaptable semanticAdapter, View containerView,
			String semanticHint, int index, boolean persisted, PreferencesHint preferencesHint) {
		Element domainElement = (Element)getSemanticElement(semanticAdapter);
		View view = element.createView(containerView, domainElement, preferencesHint);
		
		ViewUtil.insertChildView(containerView, view, index, persisted);
		
		return view;
	}
	
	protected EObject getSemanticElement(IAdaptable semanticAdapter) {
		if (semanticAdapter == null) {
			return null;
		}
		EObject eObject = (EObject) semanticAdapter.getAdapter(EObject.class);
		if (eObject != null) {
			return EMFCoreUtil.resolve(TransactionUtil.getEditingDomain(eObject), eObject);
		}
		return null;
	}
}